package com.lap.framework.generator.application;

import com.lap.framework.generator.application.assembler.Assembler;
import com.lap.framework.generator.configure.ConfigLoader;
import com.lap.framework.generator.configure.EnvConfig;
import com.lap.framework.generator.domain.FileModel;
import com.lap.framework.generator.domain.repository.FileWriter;
import com.lap.framework.generator.domain.template.CodeContext;
import com.lap.framework.generator.domain.template.CodeTemplate;
import com.lap.framework.generator.domain.valueobject.CodeType;
import com.lap.framework.generator.infra.persistence.FileWriteImpl;
import com.lap.framework.generator.infra.query.DataSourceFactory;
import com.lap.framework.generator.infra.query.MetadataDialectFactory;
import com.lap.framework.generator.infra.query.meta.TableMeta;
import com.lap.framework.generator.infra.query.strategy.MetadataDialect;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@SuppressWarnings("all")
public class CodeGenerator {

  public void generate() {
    EnvConfig config = ConfigLoader.load();
    try (Connection connection = DataSourceFactory.createConnection(config.getJdbc())) {
      MetadataDialect dialect = MetadataDialectFactory.resolve(config.getJdbc());
      List<TableMeta> tableMetaList =
          dialect.readTableMeta(connection, config.getJdbc().getTable().getIncludes());

      List<FileModel> result = getCoreFiles(tableMetaList, config);
      FileModel message = getMessages(tableMetaList, config);
      result.add(message);

      FileWriter fileWriter = new FileWriteImpl();
      fileWriter.write(result);
    } catch (SQLException e) {
      throw new RuntimeException(e);
    }
  }

  private List<FileModel> getCoreFiles(List<TableMeta> tableMetaList, EnvConfig config) {
    List<CodeType> list =
        List.of(CodeType.APP_SERVICE, CodeType.DRIVEN, CodeType.DOMAIN_MODEL, CodeType.DRIVING);

    List<FileModel> result = new ArrayList<>();
    forEachTableAndCodeType(
        tableMetaList,
        list,
        config,
        (context, generators) ->
            generators.forEach(generator -> result.addAll(generator.generate(context))));
    return result;
  }

  private FileModel getMessages(List<TableMeta> tableMetaList, EnvConfig config) {
    List<CodeType> list = List.of(CodeType.MESSAGE);
    StringBuilder result = new StringBuilder();
    forEachTableAndCodeType(
        tableMetaList,
        list,
        config,
        (context, generators) ->
            generators.forEach(
                generator -> {
                  List<String> messages = generator.generate(context);
                  result.append("\n").append("##########").append("\n");
                  result.append("#").append(context.tableMeta().getTableComment());
                  result.append("\n").append("##########").append("\n");
                  result.append(messages.stream().collect(Collectors.joining("\n")));
                }));
    return FileModel.builder()
        .path(".")
        .fileName("ValidationMessages.properties")
        .content(result.toString())
        .build();
  }

  private void forEachTableAndCodeType(
      List<TableMeta> tableMetaList,
      List<CodeType> codeTypes,
      EnvConfig config,
      CodeGeneratorAction action) {
    codeTypes.forEach(
        codeType ->
            tableMetaList.forEach(
                tableMeta -> {
                  CodeContext context = new CodeContext(tableMeta, config);
                  List<CodeTemplate> generators = Assembler.getGenerator(codeType);
                  action.execute(context, generators);
                }));
  }

  @FunctionalInterface
  private interface CodeGeneratorAction {
    void execute(CodeContext context, List<CodeTemplate> generators);
  }
}
